﻿'*******************************************************************************************
'* Copyright Brügmann Software GmbH, Papenburg
'* Author: Björn Kremer
'* Contact: codeplex<at>patorg.de
'* 
'* VB.CMIS is a VB.NET implementation of the Content Management Interoperability Services (CMIS) standard
'*
'* This file is part of VB.CMIS.
'* 
'* VB.CMIS is free software: you can redistribute it and/or modify
'* it under the terms of the GNU Lesser General Public License as published by
'* the Free Software Foundation, either version 3 of the License, or
'* (at your option) any later version.
'* 
'* VB.CMIS is distributed in the hope that it will be useful,
'* but WITHOUT ANY WARRANTY; without even the implied warranty of
'* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'* GNU Lesser General Public License for more details.
'* 
'* You should have received a copy of the GNU Lesser General Public License
'* along with VB.CMIS. If not, see <http://www.gnu.org/licenses/>.
'*******************************************************************************************

Imports System.ServiceModel.Syndication
Imports System.Xml
Imports System.Xml.Serialization
Imports CMISObjectModel.TypeDefinitions
Imports CMISObjectModel.Constants
Imports CMISObjectModel.CMISObjects
Namespace Syndication
   <Serializable()> _
   Public Class CmisSyndicationItem
      Inherits SyndicationItem
#Region "Constrcutors"

      Public Sub New()

      End Sub

      ''' <summary>
      ''' Converts the given data to a 'CmisSyndicationItem' and adds the item to this instance.
      ''' </summary>
      ''' <param name="typeDefinition"></param>
      ''' <param name="baseUri"></param>
      ''' <param name="repositoryId"></param>
      ''' <param name="performeRecursive"></param>
      ''' <param name="links"></param>
      ''' <remarks></remarks>
      Public Sub New(typeDefinition As Atom.CMISAtomEntry(Of TypeDefinition), baseUri As Uri, repositoryId As String, Optional performeRecursive As Boolean = False, Optional links As List(Of SyndicationLink) = Nothing)
         Me.BaseUri = baseUri
         'Set namespaces
         SetInitData()

         'Merge the given data into this instance.
         CreateEntry(typeDefinition, repositoryId)

         If performeRecursive Then
            'If this is a 'descendants' request the given child objects must be processed.
            ProcessRecursive(typeDefinition, repositoryId, links)
         End If
      End Sub
      ''' <summary>
      ''' Converts the given data to a 'CmisSyndicationItem' and adds the item to this instance.
      ''' </summary>
      ''' <param name="cmisObject"></param>
      ''' <param name="baseUri"></param>
      ''' <param name="RepositoryInfo"></param>
      ''' <param name="performeRecursive"></param>
      ''' <param name="links"></param>
      ''' <remarks></remarks>
      Public Sub New(cmisObject As Atom.CMISAtomEntryCmisObject, baseUri As Uri, RepositoryInfo As CMISObjectModel.ServiceDocument.RepositoryInfo, Optional performeRecursive As Boolean = False, Optional links As List(Of SyndicationLink) = Nothing)
         Me.BaseUri = baseUri
         'Set namespaces
         SetInitData()

         'Merge the given data into this instance.
         CreateEntry(cmisObject, RepositoryInfo)

         If performeRecursive Then
            'If this is a 'descendants' request the given child objects must be processed.
            ProcessRecursive(cmisObject, RepositoryInfo, links)
         End If

      End Sub
      ''' <summary>
      ''' 'If this is a 'descendants' request the given child objects must be processed.
      ''' </summary>
      ''' <param name="TypeDefinition"></param>
      ''' <param name="repositoryId"></param>
      ''' <param name="links"></param>
      ''' <remarks></remarks>
      Private Sub ProcessRecursive(TypeDefinition As Atom.CMISAtomEntry(Of TypeDefinition), repositoryId As String, links As List(Of SyndicationLink))
         If TypeDefinition.Children IsNot Nothing AndAlso TypeDefinition.Children.Entries IsNot Nothing AndAlso TypeDefinition.Children.Entries.Count > 0 Then
            'Build needed atom links
            Dim newLinks As New List(Of SyndicationLink)
            If Not String.IsNullOrEmpty(TypeDefinition.Object.ParentId) Then
               Dim parent As New Uri(BaseUri, ServiceURIs.GetTypeDefinition.Replace("{repositoryId}", repositoryId).Replace("{id}", TypeDefinition.Object.ParentId).TrimStart("/"c))
               newLinks.Add(CmisSyndicationFeed.GetUpLink(parent, CmisContentTypeConstants.Entry))
            End If
            Dim children As New Uri(BaseUri, ServiceURIs.GetTypeChildrenBase1.Replace("{repositoryId}", repositoryId).Replace("{id}", TypeDefinition.Object.Id).TrimStart("/"c))
            Dim descendants As New Uri(BaseUri, ServiceURIs.GetTypeDescendantsBase1.Replace("{repositoryId}", repositoryId).Replace("{id}", TypeDefinition.Object.Id).TrimStart("/"c))

            newLinks.Add(CmisSyndicationFeed.GetDownLink(children))
            newLinks.Add(CmisSyndicationFeed.GetDownDescLink(descendants))

            'Create a new SyndicationFeed from the current children list.
            Dim feed As New CmisSyndicationFeed(TypeDefinition.Children, True, BaseUri, repositoryId, newLinks)

            Dim formatter As New Atom10FeedFormatter(feed)

            'Serializer Object
            Dim serializer As New XmlSerializer(formatter.GetType)

            'Namspaces must not be serialized
            Dim ns As New XmlSerializerNamespaces
            ns.Add("", "")
            Dim settings As New XmlWriterSettings()
            'Declaration must not be serialized
            settings.OmitXmlDeclaration = True
            Dim element As XElement
            'Serialize the feed object and convert it to a xelement.
            Using stream As New IO.StringWriter
               Using writer As XmlWriter = XmlWriter.Create(stream, settings)
                  serializer.Serialize(writer, formatter, ns)
               End Using
               stream.Flush()
               element = XElement.Parse(stream.ToString)

            End Using
            Me.ElementExtensions.Add("children", CmisNamespaceConstants.Cmisra, element)
         End If
      End Sub
      ''' <summary>
      ''' 'If this is a 'descendants' request the given child objects must be processed.
      ''' </summary>
      ''' <param name="CmisObject"></param>
      ''' <param name="repositoryInfo"></param>
      ''' <param name="links"></param>
      ''' <remarks></remarks>
      Private Sub ProcessRecursive(CmisObject As Atom.CMISAtomEntryCmisObject, repositoryInfo As CMISObjectModel.ServiceDocument.RepositoryInfo, links As List(Of SyndicationLink))
         If CmisObject.Children IsNot Nothing AndAlso CmisObject.Children.Entries IsNot Nothing AndAlso CmisObject.Children.Entries.Count > 0 Then
            Dim repositoryId As String = repositoryInfo.RepositoryId

            'Build needed atom links
            Dim newLinks As New List(Of SyndicationLink)

            Dim parent As New Uri(BaseUri, ServiceURIs.GetObjectParentsBase.Replace("{repositoryId}", repositoryId).Replace("{id}", CmisObject.Object.Id).TrimStart("/"c))
            Dim children As New Uri(BaseUri, ServiceURIs.GetChildrenBase1.Replace("{repositoryId}", repositoryId).Replace("{id}", CmisObject.Object.Id).TrimStart("/"c))
            If repositoryInfo.Capabilities.GetDescendants Then
               Dim descendants As New Uri(BaseUri, ServiceURIs.GetDescendantsBase1.Replace("{repositoryId}", repositoryId).Replace("{id}", CmisObject.Object.Id).TrimStart("/"c))
               newLinks.Add(CmisSyndicationFeed.GetDownDescLink(descendants))
            End If
            newLinks.Add(CmisSyndicationFeed.GetUpLink(parent, CmisContentTypeConstants.Feed))
            newLinks.Add(CmisSyndicationFeed.GetDownLink(children))

            'Create a new SyndicationFeed from the current children list.
            Dim feed As New CmisSyndicationFeed(CmisObject.Children, True, BaseUri, repositoryInfo, newLinks)
            Dim formatter As New Atom10FeedFormatter(feed)

            'Serializer Object
            Dim serializer As New XmlSerializer(formatter.GetType)

            'Namspaces must not be serialized
            Dim ns As New XmlSerializerNamespaces
            ns.Add("", "")
            Dim settings As New XmlWriterSettings()
            'Declaration must not be serialized
            settings.OmitXmlDeclaration = True
            Dim element As XElement
            'Serialize the feed object and convert it to a xelement.
            Using stream As New IO.StringWriter
               Using writer As XmlWriter = XmlWriter.Create(stream, settings)
                  serializer.Serialize(writer, formatter, ns)
               End Using
               stream.Flush()
               element = XElement.Parse(stream.ToString)

            End Using
            Me.ElementExtensions.Add("children", CmisNamespaceConstants.Cmisra, element)
         End If
      End Sub
      ''' <summary>
      ''' Adds needed namespaces
      ''' </summary>
      ''' <remarks></remarks>
      Private Sub SetInitData()
         Me.AttributeExtensions.Add(New XmlQualifiedName("cmis", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.Cmis)
         Me.AttributeExtensions.Add(New XmlQualifiedName("cmism", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.Cmism)
         Me.AttributeExtensions.Add(New XmlQualifiedName("atom", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.Atom)
         Me.AttributeExtensions.Add(New XmlQualifiedName("app", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.App)
         Me.AttributeExtensions.Add(New XmlQualifiedName("cmisra", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.Cmisra)
         Me.AttributeExtensions.Add(New XmlQualifiedName("xsi", CmisNamespaceConstants.Xmlns), CmisNamespaceConstants.W3instance)


      End Sub
#End Region


      ''' <summary>
      ''' Adds the given links to this instance.
      ''' </summary>
      ''' <param name="links"></param>
      ''' <remarks></remarks>
      Private Sub AddLinks(links As List(Of CMISLink))
         For Each link As CMISLink In links
            Dim tmpLink As New SyndicationLink() With { _
              .Uri = link.Uri, _
              .RelationshipType = link.Rel, _
              .MediaType = link.MediaType _
             }
            Me.Links.Add(tmpLink)
         Next
      End Sub

      ''' <summary>
      ''' Adds the pathsegment to this instance
      ''' </summary>
      ''' <param name="relativePathSegment"></param>
      ''' <remarks></remarks>
      Private Sub AddPathSegment(relativePathSegment As String)
         Me.ElementExtensions.Add("pathSegment", CmisNamespaceConstants.Cmisra, relativePathSegment)
      End Sub

      ''' <summary>
      ''' Adds the relativepathsegment to this instance
      ''' </summary>
      ''' <param name="relativePathSegment"></param>
      ''' <remarks></remarks>
      Private Sub AddRelativePathSegment(relativePathSegment As String)
         Me.ElementExtensions.Add("relativePathSegment", CmisNamespaceConstants.Cmisra, relativePathSegment)
      End Sub



#Region "Factory Methods"
      ''' <summary>
      ''' Merge the given Data into this instance
      ''' </summary>
      ''' <param name="cmisAtomEntry"></param>
      ''' <param name="repositoryId"></param>
      ''' <remarks></remarks>
      Private Sub CreateEntry(cmisAtomEntry As Atom.CMISAtomEntry(Of TypeDefinition), repositoryId As String)
         If cmisAtomEntry Is Nothing Then
            Exit Sub
         End If
         Dim typeDefinition As TypeDefinition = cmisAtomEntry.Object

         Me.Id = cmisAtomEntry.AtomId
         Me.LastUpdatedTime = cmisAtomEntry.AtomLastUpdatedTime
         Me.Title = New TextSyndicationContent(cmisAtomEntry.AtomTitle)
         Me.PublishDate = cmisAtomEntry.AtomCreationDate
         Me.ElementExtensions.Add("edited", CmisNamespaceConstants.App, XmlConvert.ToString(cmisAtomEntry.AtomLastUpdatedTime))
         If cmisAtomEntry.AtomAuthors IsNot Nothing Then
            For Each p As SyndicationPerson In cmisAtomEntry.AtomAuthors
               Me.Authors.Add(p)
            Next
         End If
         Me.AddLinks(typeDefinition.GetAtomLinks(BaseUri, repositoryId))
         Me.ElementExtensions.Add(typeDefinition)


      End Sub

      ''' <summary>
      ''' Merge the given Data into this instance
      ''' </summary>
      ''' <param name="cmisAtomEntry"></param>
      ''' <param name="repositoryInfo"></param>
      ''' <remarks></remarks>
      Private Sub CreateEntry(cmisAtomEntry As Atom.CMISAtomEntryCmisObject, repositoryInfo As CMISObjectModel.ServiceDocument.RepositoryInfo)
         If cmisAtomEntry Is Nothing Then
            Exit Sub
         End If

         Dim cmisObject As CmisObject = cmisAtomEntry.Object

         Me.Id = cmisAtomEntry.AtomId
         Me.LastUpdatedTime = cmisAtomEntry.AtomLastUpdatedTime
         Me.Title = New TextSyndicationContent(cmisAtomEntry.AtomTitle)
         Me.PublishDate = cmisAtomEntry.AtomCreationDate
         Me.ElementExtensions.Add("edited", CmisNamespaceConstants.App, XmlConvert.ToString(cmisAtomEntry.AtomLastUpdatedTime))
         If cmisAtomEntry.AtomAuthors IsNot Nothing Then
            For Each p As SyndicationPerson In cmisAtomEntry.AtomAuthors
               Me.Authors.Add(p)
            Next
         End If
         'Is the object is a document add the Content link
         If cmisObject.GetType Is GetType(CmisDocumentObject) Then
            Dim tmpObject As CmisDocumentObject = DirectCast(cmisObject, CmisDocumentObject)
            Dim link As Uri = tmpObject.GetContentLink(BaseUri, repositoryInfo.RepositoryId)
            If link IsNot Nothing Then
               Me.Content = New UrlSyndicationContent(link, CmisContentTypeConstants.Stream)
            End If
            If cmisAtomEntry.CMISRAContent IsNot Nothing Then
               Me.ElementExtensions.Add(cmisAtomEntry.CMISRAContent)
            End If
            If cmisAtomEntry.AtomContent IsNot Nothing Then
               Me.ElementExtensions.Add(cmisAtomEntry.AtomContent)
            End If
         End If

         Me.AddLinks(cmisObject.GetAtomLinks(BaseUri, repositoryInfo))
         Me.ElementExtensions.Add(cmisObject)


         'Add relative paths if submitted
         If Not String.IsNullOrEmpty(cmisAtomEntry.RelativePathSegment) Then
            Me.AddRelativePathSegment(cmisAtomEntry.RelativePathSegment)
         End If


         If Not String.IsNullOrEmpty(cmisAtomEntry.PathSegment) Then
            Me.AddPathSegment(cmisAtomEntry.PathSegment)
         End If

         If cmisAtomEntry.BulkUpdate IsNot Nothing Then
            Me.ElementExtensions.Add(cmisAtomEntry.BulkUpdate)
         End If

      End Sub

#End Region
      Public Shared Function ParseType(entryNode As Xml.XmlNode) As Atom.CMISAtomEntryTypeDefinition
         Using stream As New IO.StringReader(entryNode.OuterXml)
            Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(Atom.CMISAtomEntryTypeDefinition))
            Return DirectCast(serializer.Deserialize(stream), Atom.CMISAtomEntryTypeDefinition)
         End Using
      End Function
      Private Shared Function Parse(Of T)(node As Xml.XmlNode) As T
         Using stream As New IO.StringReader(node.OuterXml)
            Dim serializer As New Serialization.XmlSerializer(GetType(T))
            Return DirectCast(serializer.Deserialize(stream), T)
         End Using
      End Function
      Public Shared Function ParseEntry(entryNode As Xml.XmlNode) As Object
         For Each child As XmlNode In entryNode.ChildNodes
            Dim lName As String = CMISObjectModel.Common.GetNodeNameWithoutPrefix(child).ToLowerInvariant
            If lName.Equals("object") OrElse lName.Equals("bulkupdate") Then
               Return ParseObject(entryNode)
            ElseIf lName.Equals("type") Then
               Return ParseType(entryNode)
            End If
         Next
         Return Nothing
      End Function
      Public Shared Function ParseObject(entryNode As Xml.XmlNode) As Atom.CMISAtomEntryCmisObject
         Using stream As New IO.StringReader(entryNode.OuterXml)
            Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(Atom.CMISAtomEntryCmisObject))
            Return DirectCast(serializer.Deserialize(stream), Atom.CMISAtomEntryCmisObject)
         End Using
      End Function



   End Class
End Namespace